package com.example.shoppinglist;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.koushikdutta.async.future.Future;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.ion.Ion;
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;

import android.content.Context;
import android.net.Uri;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ShoppingItemListAdapter extends ArrayAdapter<ShoppingItem> {

	private final int INVALID_ID = -1;
	private int itemLayoutId;
	private List<ShoppingItem> items;
	private FragmentActivity activity;	
	private int listId;
	private ShoppingListFragment frag;
	private HashMap<ShoppingItem, Integer> mIdMap = new HashMap<ShoppingItem, Integer>();

	public ShoppingItemListAdapter(ShoppingListFragment frag, FragmentActivity activity, int itemLayoutId, List<ShoppingItem> items, int listId) {
		super(activity, itemLayoutId, items);
		this.frag = frag;
		this.itemLayoutId = itemLayoutId;
		this.items = items;
		this.activity = activity;
		this.listId = listId;

		for (int i = 0; i < items.size(); ++i) {
			mIdMap.put(items.get(i), i);
		}
	}

	@Override
	public long getItemId(int position) {
		if (position < 0 || position >= mIdMap.size()) {
			return INVALID_ID;
		}
		ShoppingItem item = getItem(position);
		return mIdMap.get(item);
	}

	public void rebuildMap() {
		mIdMap.clear();
		for (int i = 0; i < items.size(); ++i) {
			mIdMap.put(items.get(i), i);
		}
	}

	@Override
	public boolean hasStableIds() {
		return true;
	}

	@Override
	public int getCount() {
		return items.size();
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder;
		if (convertView == null) {
			holder = new ViewHolder();
			LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = inflater.inflate(itemLayoutId, parent, false);
			holder.mainView = convertView;
			holder.imageView = (ImageView) convertView.findViewById(R.id.circle_image);
			holder.remove = (ImageView) convertView.findViewById(R.id.remove);
			holder.nameEdit = (TextView) convertView.findViewById(R.id.name_edit);
			holder.costEdit = (TextView) convertView.findViewById(R.id.cost_edit);
			convertView.setTag(holder);
		}else{
			holder = (ViewHolder) convertView.getTag();
		}
		ShoppingItem item = items.get(position);
		String currency = ((ShoppingListActivity) activity).getCurrency(listId);
		DecimalFormat df2 = new DecimalFormat(currency + "#0.00");

		holder.nameEdit.setText(item.getName());
		holder.costEdit.setText(df2.format(item.getCost() == null ? BigDecimal.ZERO : item.getCost()));
		holder.remove.setOnClickListener(new RemoveClickListener(item.getId()));
		getUrl(item, holder);

		return convertView;
	}

	private void getUrl(ShoppingItem item, ViewHolder holder) {
		//if the items current name and the item's name in the holder are different 
		//then a new image has to be downloaded/set
		if(!item.getName().equals(holder.name)){
			holder.name = item.getName();
			//making the loading image rotate to show image is downloading
			float imageCenter = getContext().getResources().getDimension(R.dimen.image_height)/2;
			RotateAnimation anim = new RotateAnimation(0f, 350f, imageCenter, imageCenter);
			//interpolator is the style of animation, so linear is a smooth one, not stop start or speeding up etc.
			anim.setInterpolator(new LinearInterpolator());
			anim.setRepeatCount(Animation.INFINITE);
			//how long it takes to do a single rotation/animation
			anim.setDuration(700);
			holder.imageView.startAnimation(anim);
			//to stop race conditions where threads running in the background 
			// might interfere with each other, like an old slow thread messing with a new one.
			if(holder.loader != null){
				holder.loader.cancel(true);
			}
			if(item.getUrl() == null || item.getUrl().isEmpty()){
				//if there is no url returned from the query, 
				//it gets a url by sending out a query to google
				//whilst setting the image view to the update icon 
				//so that there is no chance of a user seeing the wrong image
				//informs user that something is going on
				Picasso.with(getContext())
					.load(R.drawable.update)
					.transform(new CircleTransform())
					.into(holder.imageView);
				// callback informs us of whether the query fails or not
				//informs us of what has been returned
				//Ion has helpful options for getting the json response from google (that will contain the url)
				holder.loader = Ion.with(getContext())
					.load(String.format("https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=%s&start=%d&imgsz=medium", Uri.encode(item.getName()), 1))
					// get the results as json
					.asJsonObject()
					.setCallback(new ItemUrlCallBack(item, holder.imageView));
			}else{
				//picasso downloads the image from the url and sets it
				Picasso.with(getContext())
					.load(item.getUrl())
					.transform(new CircleTransform())
					.placeholder(R.drawable.update)
					.error(R.drawable.high_importance)
					.into(holder.imageView, new SimpleCallBack(holder.imageView));
			}
		}
	}

	public class SimpleCallBack implements Callback{
		//controlling when the animation stops
		private ImageView imageView;

		public SimpleCallBack(ImageView imageView) {
			this.imageView = imageView;
		}

		@Override
		public void onError() {
			imageView.setAnimation(null);
		}

		@Override
		public void onSuccess() {
			imageView.setAnimation(null);
		}
	}

	public class ItemUrlCallBack implements FutureCallback<JsonObject> {

		private ShoppingItem item;
		private ImageView imageView;

		public ItemUrlCallBack(ShoppingItem item, ImageView imageView){
			//have the item so can set and save the url for future use
			this.item = item;
			this.imageView = imageView;
    	}

		@Override
		public void onCompleted(Exception e, JsonObject result) {
			try{
				if(e != null){
					throw e;
				}
				// find the results and set them
				JsonArray results = result.getAsJsonObject("responseData").getAsJsonArray("results");
				item.setUrl(results.get(0).getAsJsonObject().get("url").getAsString());
				//picasso downloads the image from the url, sets it and the error and placeholder images.
				Picasso.with(getContext())
					.load(item.getUrl())
					.transform(new CircleTransform())
					.placeholder(R.drawable.update)
					.error(R.drawable.high_importance)
					.into(imageView, new Callback(){
						//don't want the error to spin as it would look daft
						@Override
						public void onError() {
							imageView.setAnimation(null);
						}
						//don't want the image to spin
						@Override
						public void onSuccess() {
							imageView.setAnimation(null);
						}
					});
			}
			catch(Exception ex) {
				//Log.e("Trolley",  result.getAsString());
			}
		}
	}

	public class ViewHolder {
		public String name;
		public Future<JsonObject> loader;
		public View mainView;
		public TextView costEdit;
		public TextView nameEdit;
		public ImageView remove;
		public ImageView imageView;
	}

	public class RemoveClickListener implements OnClickListener{
		//to remove an item from the list if the delete icon is clicked
		private long itemId;

		public RemoveClickListener(long itemId){
			this.itemId = itemId;
		}

		@Override
		public void onClick(View v) {
			frag.deleteItem(itemId);
			rebuildMap();
			notifyDataSetChanged();
		}
	}
}